package com.erp.base.base;

import java.beans.PropertyDescriptor;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.validation.Validator;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.erp.base.common.utils.BeanValidators;
import com.erp.base.common.utils.JsonMapper;

;
  

/**
 * 控制器支持类
 * @author whw
 */
public abstract class BaseController {

	/**
	 * 日志对象
	 */
	protected Logger logger = LoggerFactory.getLogger(getClass());

	
	/**
	 * 验证Bean实例对象
	 */
	@Autowired
	protected Validator validator;
	
	
	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 message 中
	 */
	protected boolean beanValidator(Model model, Object object, Class<?>... groups) {
		try{
			BeanValidators.validateWithException(validator, object, groups);
		}catch(ConstraintViolationException ex){
			List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
			list.add(0, "数据验证失败：");
			addMessage(model, list.toArray(new String[]{}));
			return false;
		}
		return true;
	}
	
	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 flash message 中
	 */
	protected boolean beanValidator(RedirectAttributes redirectAttributes, Object object, Class<?>... groups) {
		try{
			BeanValidators.validateWithException(validator, object, groups);
		}catch(ConstraintViolationException ex){
			List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
			list.add(0, "数据验证失败：");
			addMessage(redirectAttributes, list.toArray(new String[]{}));
			return false;
		}
		return true;
	}
	
	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组，不传入此参数时，同@Valid注解验证
	 * @return 验证成功：继续执行；验证失败：抛出异常跳转400页面。
	 */
	protected void beanValidator(Object object, Class<?>... groups) {
		BeanValidators.validateWithException(validator, object, groups);
	}
	
	/**
	 * 添加Model消息
	 * @param message
	 */
	protected void addMessage(Model model, String... messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages){
			sb.append(message).append(messages.length>1?"<br/>":"");
		}
		model.addAttribute("message", sb.toString());
	}
	
	/**
	 * 添加Flash消息
	 * @param message
	 */
	protected void addMessage(RedirectAttributes redirectAttributes, String... messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages){
			sb.append(message).append(messages.length>1?"<br/>":"");
		}
		redirectAttributes.addFlashAttribute("message", sb.toString());
	}
	
	/**
	 * 客户端返回JSON字符串
	 * @param response
	 * @param object
	 * @return
	 */
	protected void renderString(HttpServletResponse response, Object object) { 
		renderString(response, JsonMapper.toJsonString(object), "application/json");
	}
	
	/**
	 * 客户端返回字符串
	 * @param response
	 * @param string
	 * @return
	 */
	protected void renderString(HttpServletResponse response, String string, String type) {
		try { 
	        response.setContentType(type);
	        response.setCharacterEncoding("utf-8");
			response.getWriter().print(string);
		} catch (IOException e) {
			throw new RuntimeException("response 输出内容出错!",e);
		}
	}
	
	

	/**
	 * 参数绑定异常
	 */
	@ExceptionHandler({ BindException.class,
			ConstraintViolationException.class, ValidationException.class })
	public String bindException(HttpServletRequest request,
			HttpServletResponse response) {
		String ajaxHeader = request.getHeader("x-requested-with");
		if (ajaxHeader != null && "XMLHttpRequest".equals(ajaxHeader)) {
			response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
			try {
				response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException("ajax超时servlet转发异常!");
			}
			return "error/400";
		}
		return "error/400";
	}

	/**
	 * 授权登录异常
	 */
	@ExceptionHandler({ AuthenticationException.class })
	public String authenticationException(HttpServletRequest request,
			HttpServletResponse response) {
		String ajaxHeader = request.getHeader("x-requested-with");
		if (ajaxHeader != null && "XMLHttpRequest".equals(ajaxHeader)) {
			response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
			try {
				response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException("ajax超时servlet转发异常!");
			}
			return "error/403";
		}
		return "error/403";

	}

	/**
	 * 乐观锁
	 */
	@ExceptionHandler({ OptimisticLockingFailureException.class })
	public String optLockException(HttpServletRequest request,
			HttpServletResponse response) {
		String ajaxHeader = request.getHeader("x-requested-with");
		if (ajaxHeader != null && "XMLHttpRequest".equals(ajaxHeader)) {
			response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
			try {
				response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				throw new RuntimeException("ajax超时servlet转发异常!");
			}
			return "error/500";
		}

		return "error/500";
	}
	
	public void copyBeanNotNull2Bean(Object dataBean,Object toBean){
		PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(dataBean);
		for (int i = 0; i < origDescriptors.length; i++) {
			String name = origDescriptors[i].getName(); 
			if ("class".equals(name)) {
				continue;
			}
			if (PropertyUtils.isReadable(dataBean, name) &&PropertyUtils.isWriteable(toBean, name)) {
				try { 
					Object value = PropertyUtils.getSimpleProperty(dataBean, name);
					if(value!=null){
						PropertyUtils.setSimpleProperty(toBean, name, value);
					}
				}
				catch (java.lang.IllegalArgumentException ie) {
					; // Should not happen
				}
				catch (Exception e) {
					; // Should not happen
				}

			}
		}
	}

	
	/**
	 * 初始化数据绑定
	 * 1. 将所有传递进来的String进行HTML编码，防止XSS攻击
	 * 2. 将字段中Date类型转换为String类型
	 */
	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		// String类型转换，将所有传递进来的String进行HTML编码，防止XSS攻击
		binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(text == null ? null : StringEscapeUtils.escapeHtml4(text.trim()));
			}
			@Override
			public String getAsText() {
				Object value = getValue();
				return value != null ? value.toString() : "";
			}
		});
		// Date 类型转换
		binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				Date dt=null;
				try {
					dt=DateUtils.parseDate(text, parsePatterns);
				} catch (Exception e) { 
				}
				setValue(dt);
			}
//			@Override
//			public String getAsText() {
//				Object value = getValue();
//				return value != null ? DateUtils.formatDateTime((Date)value) : "";
//			}
		});
	}
	
	private static String[] parsePatterns = {
			"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", 
			"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
			"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
	
}

